﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
using Logging.LiveLogger;

namespace Calculator
{
	class NumPad
	{
		#region Constants
		
		public static readonly List<Keys> ACCEPTED_KEYS = new List<Keys>()
		{
			Keys.NumPad0, Keys.NumPad1, Keys.NumPad2, Keys.NumPad3, Keys.NumPad4,
			Keys.NumPad5, Keys.NumPad6, Keys.NumPad7, Keys.NumPad8, Keys.NumPad9,
			Keys.Add, Keys.Subtract, Keys.Multiply, Keys.Divide, Keys.Enter,
			Keys.NumLock, Keys.Decimal
		};
		
		#endregion Constants

		#region Fields

		private TableLayoutPanel mNumPad;
		private Button btn_r0c0;
		private Button btn_r0c1;
		private Button btn_r0c2;
		private Button btn_r0c3;
		private Button btn_r1c0;
		private Button btn_r1c1;
		private Button btn_r1c2;
		private Button btn_r1c3;
		private Button btn_r2c0;
		private Button btn_r2c1;
		private Button btn_r2c2;
		private Button btn_r3c0;
		private Button btn_r3c1;
		private Button btn_r3c2;
		private Button btn_r3c3;
		private Button btn_r4c0;
		private Button btn_r4c2;

		private Dictionary<MetaKeyStatus, Dictionary<Button, string>> mButtonText;
		private Dictionary<Keys, Button> mKeys;

		#endregion Fields
		
		#region Constructor
		
		public NumPad(TableLayoutPanel tlpNumPad)
		{
			// Configure the TableLayoutPanel to have the appropriate shape
			mNumPad = tlpNumPad;
			mNumPad.Dock = DockStyle.Fill;
			mNumPad.ColumnCount = 4;
			mNumPad.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
			mNumPad.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
			mNumPad.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
			mNumPad.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 25F));
			mNumPad.RowCount = 5;
			mNumPad.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
			mNumPad.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
			mNumPad.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
			mNumPad.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));
			mNumPad.RowStyles.Add(new RowStyle(SizeType.Percent, 20F));

			// Note: Button Naming Convention
			// Since the functionality of the buttons is not constant, the buttons
			// are named according to their position on the NumPad. Please see
			// this diagram for the button names and their default operations:
			//
			//	=================================================
			//	| btn_r0c0  | btn_r0c1	| btn_r0c2	| btn_r0c3	|
			//	| [NumLock]	|   [/]		|   [*]		|   [-]		|
			//	|-----------|-----------|-----------|-----------|
			//	| btn_r1c0	| btn_r1c1	| btn_r1c2	| btn_r1c3	|
			//	|   [7]		|   [8]		|   [9]		|   [+]		|
			//	|-----------|-----------|-----------|			|
			//	| btn_r2c0	| btn_r2c1	| btn_r2c2	|			|
			//	|   [4]		|   [5]		|   [6]		|			|
			//	|-----------|-----------|-----------|-----------|
			//	| btn_r3c0	| btn_r3c1	| btn_r3c2	| btn_r3c3	|
			//	|   [1]		|   [2]		|   [3]		|   [=]		|
			//	|-----------|-----------|-----------|			|
			//	| btn_r4c0				| btn_r4c2	|			|
			//	|   [0]					|   [.]		|			|
			//	|===============================================|

			// Initialize the buttons
			btn_r0c0 = new Button();
			btn_r0c0.Dock = DockStyle.Fill;
			btn_r0c0.UseVisualStyleBackColor = true;
			mNumPad.Controls.Add(btn_r0c0, 0, 0);
			btn_r0c0.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r0c1 = new Button();
			mNumPad.Controls.Add(btn_r0c1, 1, 0);
			btn_r0c1.Dock = DockStyle.Fill;
			btn_r0c1.UseVisualStyleBackColor = true;
			btn_r0c1.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r0c2 = new Button();
			mNumPad.Controls.Add(btn_r0c2, 2, 0);
			btn_r0c2.Dock = DockStyle.Fill;
			btn_r0c2.UseVisualStyleBackColor = true;
			btn_r0c2.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r0c3 = new Button();
			mNumPad.Controls.Add(btn_r0c3, 3, 0);
			btn_r0c3.Dock = DockStyle.Fill;
			btn_r0c3.UseVisualStyleBackColor = true;
			btn_r0c3.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r1c0 = new Button();
			mNumPad.Controls.Add(btn_r1c0, 0, 1);
			btn_r1c0.Dock = DockStyle.Fill;
			btn_r1c0.UseVisualStyleBackColor = true;
			btn_r1c0.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r1c1 = new Button();
			mNumPad.Controls.Add(btn_r1c1, 1, 1);
			btn_r1c1.Dock = DockStyle.Fill;
			btn_r1c1.UseVisualStyleBackColor = true;
			btn_r1c1.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r1c2 = new Button();
			mNumPad.Controls.Add(btn_r1c2, 2, 1);
			btn_r1c2.Dock = DockStyle.Fill;
			btn_r1c2.UseVisualStyleBackColor = true;
			btn_r1c2.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r1c3 = new Button();
			mNumPad.Controls.Add(btn_r1c3, 3, 1);
			btn_r1c3.Dock = DockStyle.Fill;
			btn_r1c3.UseVisualStyleBackColor = true;
			btn_r1c3.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r2c0 = new Button();
			mNumPad.Controls.Add(btn_r2c0, 0, 2);
			btn_r2c0.Dock = DockStyle.Fill;
			btn_r2c0.UseVisualStyleBackColor = true;
			btn_r2c0.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r2c1 = new Button();
			mNumPad.Controls.Add(btn_r2c1, 1, 2);
			btn_r2c1.Dock = DockStyle.Fill;
			btn_r2c1.UseVisualStyleBackColor = true;
			btn_r2c1.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r2c2 = new Button();
			mNumPad.Controls.Add(btn_r2c2, 2, 2);
			btn_r2c2.Dock = DockStyle.Fill;
			btn_r2c2.UseVisualStyleBackColor = true;
			btn_r2c2.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r3c0 = new Button();
			mNumPad.Controls.Add(btn_r3c0, 0, 3);
			btn_r3c0.Dock = DockStyle.Fill;
			btn_r3c0.UseVisualStyleBackColor = true;
			btn_r3c0.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r3c1 = new Button();
			mNumPad.Controls.Add(btn_r3c1, 1, 3);
			btn_r3c1.Dock = DockStyle.Fill;
			btn_r3c1.UseVisualStyleBackColor = true;
			btn_r3c1.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r3c2 = new Button();
			mNumPad.Controls.Add(btn_r3c2, 2, 3);
			btn_r3c2.Dock = DockStyle.Fill;
			btn_r3c2.UseVisualStyleBackColor = true;
			btn_r3c2.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r3c3 = new Button();
			mNumPad.Controls.Add(btn_r3c3, 3, 3);
			btn_r3c3.Dock = DockStyle.Fill;
			btn_r3c3.UseVisualStyleBackColor = true;
			btn_r3c3.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r4c0 = new Button();
			mNumPad.Controls.Add(btn_r4c0, 0, 4);
			btn_r4c0.Dock = DockStyle.Fill;
			btn_r4c0.UseVisualStyleBackColor = true;
			btn_r4c0.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			btn_r4c2 = new Button();
			mNumPad.Controls.Add(btn_r4c2, 2, 4);
			btn_r4c2.Dock = DockStyle.Fill;
			btn_r4c2.UseVisualStyleBackColor = true;
			btn_r4c2.Font = new Font("Courier New", 11.25F, FontStyle.Regular,
				GraphicsUnit.Point, ((byte)(0)));

			// Handle irregularly shaped buttons
			mNumPad.SetColumnSpan(btn_r4c0, 2);
			mNumPad.SetRowSpan(btn_r1c3, 2);
			mNumPad.SetRowSpan(btn_r3c3, 2);

			// Set up button text
			mButtonText =
				new Dictionary<MetaKeyStatus, Dictionary<Button, string>>();
			mButtonText.Add(MetaKeyStatus.None,
				new Dictionary<Button, string>());
			mButtonText.Add(MetaKeyStatus.Control,
				new Dictionary<Button, string>());
			mButtonText.Add(MetaKeyStatus.Alt,
				new Dictionary<Button, string>());
			mButtonText.Add(MetaKeyStatus.Shift,
				new Dictionary<Button, string>());

			mButtonText[MetaKeyStatus.None].Add(btn_r0c0, "Num Lock");
			mButtonText[MetaKeyStatus.None].Add(btn_r0c1, "/");
			mButtonText[MetaKeyStatus.None].Add(btn_r0c2, "*");
			mButtonText[MetaKeyStatus.None].Add(btn_r0c3, "-");
			mButtonText[MetaKeyStatus.None].Add(btn_r1c0, "7");
			mButtonText[MetaKeyStatus.None].Add(btn_r1c1, "8");
			mButtonText[MetaKeyStatus.None].Add(btn_r1c2, "9");
			mButtonText[MetaKeyStatus.None].Add(btn_r1c3, "+");
			mButtonText[MetaKeyStatus.None].Add(btn_r2c0, "4");
			mButtonText[MetaKeyStatus.None].Add(btn_r2c1, "5");
			mButtonText[MetaKeyStatus.None].Add(btn_r2c2, "6");
			mButtonText[MetaKeyStatus.None].Add(btn_r3c0, "1");
			mButtonText[MetaKeyStatus.None].Add(btn_r3c1, "2");
			mButtonText[MetaKeyStatus.None].Add(btn_r3c2, "3");
			mButtonText[MetaKeyStatus.None].Add(btn_r3c3, "=");
			mButtonText[MetaKeyStatus.None].Add(btn_r4c0, "0");
			mButtonText[MetaKeyStatus.None].Add(btn_r4c2, ".");

			mButtonText[MetaKeyStatus.Control].Add(btn_r0c0, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r0c1, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r0c2, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r0c3, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r1c0, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r1c1, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r1c2, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r1c3, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r2c0, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r2c1, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r2c2, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r3c0, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r3c1, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r3c2, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r3c3, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r4c0, "Control");
			mButtonText[MetaKeyStatus.Control].Add(btn_r4c2, "Control");

			mButtonText[MetaKeyStatus.Shift].Add(btn_r0c0, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r0c1, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r0c2, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r0c3, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r1c0, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r1c1, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r1c2, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r1c3, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r2c0, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r2c1, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r2c2, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r3c0, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r3c1, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r3c2, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r3c3, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r4c0, "Shift");
			mButtonText[MetaKeyStatus.Shift].Add(btn_r4c2, "Shift");

			mButtonText[MetaKeyStatus.Alt].Add(btn_r0c0, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r0c1, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r0c2, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r0c3, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r1c0, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r1c1, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r1c2, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r1c3, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r2c0, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r2c1, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r2c2, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r3c0, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r3c1, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r3c2, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r3c3, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r4c0, "Alt");
			mButtonText[MetaKeyStatus.Alt].Add(btn_r4c2, "Alt");

			// Associate Keys with buttons
			mKeys = new Dictionary<Keys,Button>();
			mKeys.Add(Keys.NumPad0, btn_r4c0);
			mKeys.Add(Keys.NumPad1, btn_r3c0);
			mKeys.Add(Keys.NumPad2, btn_r3c1);
			mKeys.Add(Keys.NumPad3, btn_r3c2);
			mKeys.Add(Keys.NumPad4, btn_r2c0);
			mKeys.Add(Keys.NumPad5, btn_r2c1);
			mKeys.Add(Keys.NumPad6, btn_r2c2);
			mKeys.Add(Keys.NumPad7, btn_r1c0);
			mKeys.Add(Keys.NumPad8, btn_r1c1);
			mKeys.Add(Keys.NumPad9, btn_r1c2);
			mKeys.Add(Keys.Add, btn_r1c3);
			mKeys.Add(Keys.Subtract, btn_r0c3);
			mKeys.Add(Keys.Multiply, btn_r0c2);
			mKeys.Add(Keys.Divide, btn_r0c1);
			mKeys.Add(Keys.Enter, btn_r3c3);
			mKeys.Add(Keys.NumLock, btn_r0c0);
			mKeys.Add(Keys.Decimal, btn_r4c2);

			UpdateButtonText(MetaKeyStatus.None);
		}

		#endregion Constructor

		#region Exposed Methods

		public void UpdateButtonText(MetaKeyStatus status)
		{
			if (mButtonText.ContainsKey(status))
			{
				foreach (Button b in mButtonText[status].Keys)
				{
					b.Text = mButtonText[status][b];
				}
			}
		}

		public void ForwardKeyDownToButton(Keys key)
		{
			LiveLogger.Trace("ForwardKeyDownToButton: " + key.ToString());
			if (mKeys.ContainsKey(key))
			{
				mKeys[key].FlatStyle = FlatStyle.Flat;
			}
		}

		public void ForwardKeyUpToButton(Keys key)
		{
			LiveLogger.Trace("ForwardKeyUpToButton: " + key.ToString());
			if (mKeys.ContainsKey(key))
			{
				mKeys[key].FlatStyle = FlatStyle.Standard;
			}
		}

		#endregion Exposed Methods
	}
}
